Nous souhaitons classifier un article du monde selon son contenu, Nous possèdons pour cela un jeu de données avec la catégorie et le contenu de 10k articles.

Pour mener notre tâche à bien nous allons effectuer un prétraitement des données textuelles par la transformation de données (textuelles) non structurées en un format de données structuré.

Et ce dans l’objectif d’appliquer des algorithmes de classifications, cela inclut la pondération et la sélection des variables(des mots).

Concrètement, il s’agit de la transformation d’un grand nombre de caractéristiques éparses en un nombre significativement plus petit de caractéristiques denses.

Nous utiliserons ainsi 3 algorithmes pour la classification dont un dans une version limitée à 25 variables explicatives.

Nous finirons par l’évaluation des résultats de la prédiction des classifications sur le jeu de test.

Import de la base de données & du jeu test

On utilise l’encodage UTF-8 car le monde est un journal français utilisant des caractères spéciaux. Le jeu de test est fournis, il a pour élément positif le fait d’être un article de type économie

data <- 
   read.csv("le_monde.csv", encoding="UTF-8", sep=";", comment.char="#")

test <- 
  read.csv("lignes_jeux_tests.csv")

transformation des données

Il est nécessaire de transformer ces données, nous n’avons qu’une unique variable explicative : le texte en entier de l’article. Cette unique variable explicative est inexploitable, nous souhaitons un “bag of words”.

Suppression des deux collones non utiles à la modélisation

data$date <- NULL
data$title <- NULL

Pour la gestion des manquants, on supprime les lignes avec des valeurs manquantes (normalement aucune supprimmé)

## integer(0)

On applique les bons types de variables

data$category <- as.factor(data$category)
data$content <- as.character(data$content)
str(data)
## 'data.frame':    10000 obs. of  2 variables:
##  $ category: Factor w/ 6 levels "culture","economie",..: 6 5 5 2 5 5 5 5 1 5 ...
##  $ content : chr  "  / L’international français Jérémy Ménez va rejoindre le club de Bordeaux en Ligue 1, en provenance du Milan A"| __truncated__ "  / Le cousin d’un des assassins du Père Jacques Hamel à Saint-Etienne-du-Rouvray, identifié comme étant Farid "| __truncated__ "  / Si le premier ministre Manuel Valls constate que « l’islam a trouvé sa place dans la République », « face à"| __truncated__ "  / Les épargnants français sont choyés. Lundi 1er août, le taux de rémunération du Livret A aurait théoriqueme"| __truncated__ ...

On retire les accents, en effet dans l’une des étapes suivantes où l’on retire les caractères qui ne sont pas des lettres, les lettres avec accents font des trous dans les mots, rendant un grand nombre de mots inexploitable.

On a besoin d’un objet de type corpus, on prend là ou sont les données, ici la collone V6. On affiche la première ligne

contenu <- Corpus(VectorSource(data$content))
contenu[1]$content
## [1] "  / L'international francais Jeremy Menez va rejoindre le club de Bordeaux en Ligue 1, en provenance du Milan AC, sous reserve de la traditionnelle visite medicale, a annonce le club aquitain dimanche. /  Menez est la troisieme recrue des Girondins apres le milieu de Monaco, Jeremy Toulalan, et l'attaquant guineen de Bastia, Francois Kamano. Bordeaux sort d'une pale saison et repart avec des ambitions nouvelles et l'entraineur Jocelyn Gourvennec, qui jouit d'une grosse cote grace a ses six saisons convaincantes a Guingamp. Age de 29 ans, Menez, qui compte 24 selections (2 buts) chez les Bleus -la derniere en 2013-, evoluait depuis deux ans au Milan AC, ou il lui restait un an de contrat, mais sa derniere saison a ete perturbee par des blessures. Forme a Sochaux, Menez fait partie de la fameuse generation 1987 championne d'Europe des U17 en 2004. Alors considere comme un des plus grands espoirs du foot francais, il avait par la suite rejoint Monaco de 2006 a 2008, puis la Roma pendant quatre saisons avant de revenir en France, au Paris-Saint-Germain en 2012. Son aventure parisienne, avec deux titres de champion a la cle, avait pris fin deux ans plus tard pour un retour en Italie, au Milan AC. Au sein de l'equipe lombarde il a realise sa meilleure saison (16 buts inscrits) en 2014-2015, avant d'etre perturbe par des blessures au dos la saison derniere qui l'ont prive de sept mois de competition, d'aout a janvier, pour ne disputer que 10 matchs (2 buts)."

On supprime les caracteres qui ne sont pas des lettres (cette étape posait problème avec les lettres à accent)

contenu <- tm_map(contenu, content_transformer(gsub), pattern = "[^a-zA-Z]", replacement = " ")
## Warning in tm_map.SimpleCorpus(contenu, content_transformer(gsub), pattern =
## "[^a-zA-Z]", : transformation drops documents
contenu[1]$content
## [1] "    L international francais Jeremy Menez va rejoindre le club de Bordeaux en Ligue    en provenance du Milan AC  sous reserve de la traditionnelle visite medicale  a annonce le club aquitain dimanche     Menez est la troisieme recrue des Girondins apres le milieu de Monaco  Jeremy Toulalan  et l attaquant guineen de Bastia  Francois Kamano  Bordeaux sort d une pale saison et repart avec des ambitions nouvelles et l entraineur Jocelyn Gourvennec  qui jouit d une grosse cote grace a ses six saisons convaincantes a Guingamp  Age de    ans  Menez  qui compte    selections    buts  chez les Bleus  la derniere en        evoluait depuis deux ans au Milan AC  ou il lui restait un an de contrat  mais sa derniere saison a ete perturbee par des blessures  Forme a Sochaux  Menez fait partie de la fameuse generation      championne d Europe des U   en       Alors considere comme un des plus grands espoirs du foot francais  il avait par la suite rejoint Monaco de      a       puis la Roma pendant quatre saisons avant de revenir en France  au Paris Saint Germain en       Son aventure parisienne  avec deux titres de champion a la cle  avait pris fin deux ans plus tard pour un retour en Italie  au Milan AC  Au sein de l equipe lombarde il a realise sa meilleure saison     buts inscrits  en            avant d etre perturbe par des blessures au dos la saison derniere qui l ont prive de sept mois de competition  d aout a janvier  pour ne disputer que    matchs    buts  "

On mets les majuscules en minuscules

contenu <- tm_map(contenu, content_transformer(tolower))
## Warning in tm_map.SimpleCorpus(contenu, content_transformer(tolower)):
## transformation drops documents
contenu[1]$content
## [1] "    l international francais jeremy menez va rejoindre le club de bordeaux en ligue    en provenance du milan ac  sous reserve de la traditionnelle visite medicale  a annonce le club aquitain dimanche     menez est la troisieme recrue des girondins apres le milieu de monaco  jeremy toulalan  et l attaquant guineen de bastia  francois kamano  bordeaux sort d une pale saison et repart avec des ambitions nouvelles et l entraineur jocelyn gourvennec  qui jouit d une grosse cote grace a ses six saisons convaincantes a guingamp  age de    ans  menez  qui compte    selections    buts  chez les bleus  la derniere en        evoluait depuis deux ans au milan ac  ou il lui restait un an de contrat  mais sa derniere saison a ete perturbee par des blessures  forme a sochaux  menez fait partie de la fameuse generation      championne d europe des u   en       alors considere comme un des plus grands espoirs du foot francais  il avait par la suite rejoint monaco de      a       puis la roma pendant quatre saisons avant de revenir en france  au paris saint germain en       son aventure parisienne  avec deux titres de champion a la cle  avait pris fin deux ans plus tard pour un retour en italie  au milan ac  au sein de l equipe lombarde il a realise sa meilleure saison     buts inscrits  en            avant d etre perturbe par des blessures au dos la saison derniere qui l ont prive de sept mois de competition  d aout a janvier  pour ne disputer que    matchs    buts  "

On retire les lettres isolés et les mots “vides” tel “quand, comme, hors …”

stopwords_fr <- stopwords("french")
stopwords_fr <- c(stopwords_fr, "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t",
                   "u","v","w","x","y","z" )

contenu <- tm_map(contenu, removeWords , stopwords_fr)
## Warning in tm_map.SimpleCorpus(contenu, removeWords, stopwords_fr):
## transformation drops documents
contenu[1]$content
## [1] "     international francais jeremy menez va rejoindre  club  bordeaux  ligue     provenance  milan ac  sous reserve   traditionnelle visite medicale   annonce  club aquitain dimanche     menez   troisieme recrue  girondins apres  milieu  monaco  jeremy toulalan    attaquant guineen  bastia  francois kamano  bordeaux sort   pale saison  repart   ambitions nouvelles   entraineur jocelyn gourvennec   jouit   grosse cote grace   six saisons convaincantes  guingamp  age     ans  menez   compte    selections    buts  chez  bleus   derniere         evoluait depuis deux ans  milan ac     restait  an  contrat    derniere saison  ete perturbee   blessures  forme  sochaux  menez fait partie   fameuse generation      championne  europe            alors considere comme   plus grands espoirs  foot francais      suite rejoint monaco              puis  roma pendant quatre saisons avant  revenir  france   paris saint germain         aventure parisienne   deux titres  champion   cle   pris fin deux ans plus tard   retour  italie   milan ac   sein   equipe lombarde   realise  meilleure saison     buts inscrits              avant  etre perturbe   blessures  dos  saison derniere    prive  sept mois  competition   aout  janvier    disputer     matchs    buts  "

Racinisation (sans retirer le premier espace)

contenu <- tm_map(contenu, stemDocument, "french")
## Warning in tm_map.SimpleCorpus(contenu, stemDocument, "french"): transformation
## drops documents
contenu[1]$content
## [1] "international franc jeremy men va rejoindr club bordeau ligu proven milan ac sous reserv traditionnel visit medical annonc club aquitain dimanch men troisiem recru girondin apre milieu monaco jeremy toulalan attaqu guineen basti francois kamano bordeau sort pal saison repart ambit nouvel entraineur jocelyn gourvennec jou gross cot grac six saison convainc guingamp age an men compt select but chez bleus dernier evolu depuis deux an milan ac rest an contrat dernier saison ete perturbe blessur form sochal men fait part fameux gener champion europ alor consider comm plus grand espoir foot franc suit rejoint monaco puis rom pend quatr saison avant reven franc paris saint germain aventur parisien deux titr champion cle pris fin deux an plus tard retour ital milan ac sein equip lombard realis meilleur saison but inscrit avant etre perturb blessur dos saison dernier priv sept mois competit aout janvi disput match but"
contenu <- tm_map(contenu , stripWhitespace)
## Warning in tm_map.SimpleCorpus(contenu, stripWhitespace): transformation drops
## documents
contenu <- tm_map(contenu, content_transformer(gsub), pattern = "^\\s+", replacement = "")
## Warning in tm_map.SimpleCorpus(contenu, content_transformer(gsub), pattern = "^\
## \s+", : transformation drops documents
contenu[1]$content
## [1] "international franc jeremy men va rejoindr club bordeau ligu proven milan ac sous reserv traditionnel visit medical annonc club aquitain dimanch men troisiem recru girondin apre milieu monaco jeremy toulalan attaqu guineen basti francois kamano bordeau sort pal saison repart ambit nouvel entraineur jocelyn gourvennec jou gross cot grac six saison convainc guingamp age an men compt select but chez bleus dernier evolu depuis deux an milan ac rest an contrat dernier saison ete perturbe blessur form sochal men fait part fameux gener champion europ alor consider comm plus grand espoir foot franc suit rejoint monaco puis rom pend quatr saison avant reven franc paris saint germain aventur parisien deux titr champion cle pris fin deux an plus tard retour ital milan ac sein equip lombard realis meilleur saison but inscrit avant etre perturb blessur dos saison dernier priv sept mois competit aout janvi disput match but"

Vectorisation

Nous ne gardons que les mots avec 1000 occurences minimum

Le traitement de text effectué, on re-ajoute les données au tableau data pour comparer le texte de départ et le texte obtenu :

Le texte obtenu est correct.

Combien de fois les mots (variables) ont d’occurence dans le contenu des articles ?

summary(colSums(base_modele))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1000    1228    1612    2229    2491   18858

On remarque une médiane à 1612 la haute valeur du maximum est surement dû à des mots vides (stop words) non retirer. Nous étudierons un modèle avec moins de variables (mots) dans une prochaine partie.

Testons notre hypothèse des stop words non retirer, en effet, il pourrait s’agir de mots apparaissant beaucoup dans une certaine catégorie d’articles. Regardons dans combien d’articles les mots sont référencés (sur 10k articles)

occurences <- apply(base_modele, 2, function(x) sum(x>0))
summary(occurences)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   364.0   871.5  1101.0  1392.7  1676.5  6237.0

Un maximum à 6237, soit 2/3 des documents. Nous verrons l’importance de ces mots dans le modèle lorsque nous réaliserons un modèle supervisé avec un maximum de 25 variables.

On construit alors notre modèle avec les catégories et les mots en variables.

base_modelisation = cbind.data.frame(data, base_modele)
base_modelisation = base_modelisation[,-2]
base_modelisation = base_modelisation[,-2]
#On prépare le jeu à 25 variables
#Somme <- colSums(base_modele)
#garder <- which(Somme > median(Somme))

Présentation des données

Variables à expliquer : culture, economie, planete, politique, societe, sport.

439 Variables explicatives : les mots qui apparaissent plus de 1000 fois.

A noter que nous n’effectuons que les dernières partie d’un projet de Data Science, puisque les données nous ont été fournis.

Avant de réaliser des modèles de prédictions, détaillons le jeu de données transformé obtenu. Notre plus grande menace serait une corrélation globale de nos variables.

Visualisons graphiquement si nos variables sont très corrélés avec une heatmap :

Les variables sont très peu corrélés,

Pour complèter cela, on réalise une analyse en composante principale avec la catégorie en variable qualitative, ainsi en affichant les ellipse nous verrons les catégories qui s’opposent et quelles variables (les mots dans notre cas) sont les plus responsables des axes, autrement dit les plus importants.

#ces deux lignes sont marginales et ne permettent pas de ce centrer sur les individus.
base_modelisation_ACP <- base_modelisation[-c(8808,5857), ]


library(FactoMineR)
res.pca = PCA(base_modelisation_ACP, scale.unit=TRUE, ncp=5, quali.sup=1, graph=T)

plot.PCA(res.pca, axes=c(1, 2), choix="ind", habillage=1,label="var")

#Essayons de dégager une tendance avec les catégories

library("factoextra")

fviz_pca_ind(res.pca, geom.ind = "point", col.ind = base_modelisation_ACP$category, 
             palette = c("#00AFBB", "#E7B800", "#FC4E07", "#33FF5E","#CC33FF", "#FFC233"  ),
             addEllipses = TRUE, ellipse.type = "confidence",
             legend.title = "Catégorie de l'article"
)

Les deux premières dimensions ne rendent compte que de 10% de la variance, les graphiques sont inexploitables. Nous pouvons affirmer que les données sont très dispersés, leur non-corrélation est très forte.

Nuage de mots pour les catégories

Une fois la non-corrélation globale de nos variables assurés, Examinons graphiquement grâce à la librairie wordcloud les mots les plus fréquents par catégorie par un nuage de mots.

#Preparation des données pour le nuage des catégories
# on concatene tout le texte , on sélectionne la catégorie sport et spécicifie content_modif pour là où on prend le texte.
motSport <- paste(data[data$category=="sport",'content_modif'],collapse=' ') 
motSociete <- paste(data[data$category=="societe",'content_modif'],collapse=' ') 
motEconomie <- paste(data[data$category=="economie",'content_modif'],collapse=' ') 
motCulture <- paste(data[data$category=="culture",'content_modif'],collapse=' ') 
motPolitique <- paste(data[data$category=="politique",'content_modif'],collapse=' ') 
motPlanete <- paste(data[data$category=="planete",'content_modif'],collapse=' ') 

# on compte chaque mot, le motif entre guillemet veut dire qu'on coupe la #chainedecaractère quelque soit le nombre d'espaces entre les mots, decreasing en true car il faut montrer les most les plus fréquents , donc on met en décroissant (voir la doc de sort)
motsFreqSport <- data.frame(sort(table(strsplit(motSport,"\\s+")),decreasing = TRUE )) 
motsFreqSociete <- data.frame(sort(table(strsplit(motSociete,"\\s+")),decreasing = TRUE )) 
motsFreqEconomie <- data.frame(sort(table(strsplit(motEconomie,"\\s+")),decreasing = TRUE )) 
motsFreqCulture <- data.frame(sort(table(strsplit(motCulture,"\\s+")),decreasing = TRUE )) 
motsFreqPolitique <- data.frame(sort(table(strsplit(motPolitique,"\\s+")),decreasing = TRUE )) 
motsFreqPlanete <- data.frame(sort(table(strsplit(motPlanete,"\\s+")),decreasing = TRUE )) 

Création des nuages de mots

Sport

wordcloud2(data = motsFreqSport[1:500,],minSize = 5, size = 3,shape = 'star',color = "random-light", backgroundColor = "grey")

Societe

wordcloud2(data = motsFreqSociete[1:500,],minSize = 5, size = 3,shape = 'star',color = "random-light", backgroundColor = "grey")

Economie

wordcloud2(data = motsFreqEconomie[1:500,],minSize = 5, size = 3,shape = 'star',color = "random-light", backgroundColor = "grey")

Culture

wordcloud2(data = motsFreqCulture[1:500,],minSize = 5, size = 3,shape = 'star',color = "random-light", backgroundColor = "grey")

Politique

wordcloud2(data = motsFreqPolitique[1:500,],minSize = 5, size = 3,shape = 'star',color = "random-light", backgroundColor = "grey")

Planete

wordcloud2(data = motsFreqPlanete[1:500,],minSize = 5, size = 3,shape = 'star',color = "random-light", backgroundColor = "grey")

De nombreux mots semblent spécifiques à une seule catégorie, nous devrions obtenir de bons indicateurs de prédiction.

Avant de passer à la partie suivante, supprimons les données que nous n’utiliserons plus

Modèle Supervisé

Apprentissage supervisé: expliquer/prédire une sortie Y à partir d’entrées X Nous devons éviter le sur-apprentissage.

Modèle supervisé pouvant être utilisé : CART , Randomforest, Validation croisée

On commence par construire un modèle d’apprentissage, composé de 80% des lignes de base_modelisation. Le jeu de test est quand à lui fourni.

nb_lignes <- sample(1:nrow(base_modelisation), nrow(base_modelisation)*0.80)

Premier modèle : CART

Modélisation : Arbre, algorithme : CART Notre premier modèle est un arbre de décision.

Le principe est que, tant qu’on a pas atteind la taille minimal de noeuds enfants on recherche un seuil qui permet de séparer le noeud parents en 2 noeuds enfants en maximisant notre critère de répartition/de fractionnement.

Notre critère de répartition est le GINI, il est par défaut dans la fonction rpart.

On prend un cp choisi arbitrairement.

tree <-rpart(category~. ,
             data = base_modelisation[nb_lignes,],
             cp=0,
             minsplit = 10
            # ,control = rpart.control(minsplit = 10)
             )

visTree(tree)

On recherche le cp optimal.

plotcp(tree)

On affine la prédiction en choisissant l’arbre avec l’erreur de prédiction la plus basse

Meilleur <- which.min(tree$cptable[,"xerror"])
cpBest <- tree$cptable[Meilleur, "CP"]
ArbreChoisi <- prune(tree, cp = cpBest)
visTree(ArbreChoisi)
#Mauvaise méthode puisque le meilleur cp change d'une exécution à l'autre du code
#Besttree <-rpart(category~. ,
#                 data = base_modelisation[nb_lignes,],
#                cp=8e-04,
#               minsplit = 10
                   # ,control = rpart.control(minsplit = 10)
                 
#              )

#visTree(Besttree)


#print(Besttree$cptable)
#attributes(Besttree)
#construction plot
#plot(Besttree)
#text(Besttree, use.n=T)

Evaluation, matrice de confusion :

prediction_categorie <- predict(ArbreChoisi,
             newdata=base_modelisation[-nb_lignes,],
           # newdata=test,  
           #trouver un moyen d'utiliser le jeu de test
            type= "class"
           
           )
length(prediction_categorie)
## [1] 2000
conf <- confusionMatrix(data=prediction_categorie, reference = base_modelisation[-nb_lignes,]$category)
conf
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  culture economie planete politique societe sport
##   culture       321       48      49        31     121    57
##   economie       29      207      33        26      53    11
##   planete         3        1      22         1       7     2
##   politique      11       30      18       132      56     9
##   societe        36       75      39        77     300    12
##   sport          11       14       9         8      12   129
## 
## Overall Statistics
##                                           
##                Accuracy : 0.5555          
##                  95% CI : (0.5334, 0.5774)
##     No Information Rate : 0.2745          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.4435          
##                                           
##  Mcnemar's Test P-Value : < 2.2e-16       
## 
## Statistics by Class:
## 
##                      Class: culture Class: economie Class: planete
## Sensitivity                  0.7810          0.5520         0.1294
## Specificity                  0.8074          0.9065         0.9923
## Pos Pred Value               0.5120          0.5766         0.6111
## Neg Pred Value               0.9345          0.8976         0.9246
## Prevalence                   0.2055          0.1875         0.0850
## Detection Rate               0.1605          0.1035         0.0110
## Detection Prevalence         0.3135          0.1795         0.0180
## Balanced Accuracy            0.7942          0.7292         0.5609
##                      Class: politique Class: societe Class: sport
## Sensitivity                    0.4800         0.5464       0.5864
## Specificity                    0.9281         0.8353       0.9697
## Pos Pred Value                 0.5156         0.5566       0.7049
## Neg Pred Value                 0.9180         0.8296       0.9499
## Prevalence                     0.1375         0.2745       0.1100
## Detection Rate                 0.0660         0.1500       0.0645
## Detection Prevalence           0.1280         0.2695       0.0915
## Balanced Accuracy              0.7041         0.6909       0.7780

AUC

library(ROCR)
library(pROC)
## Type 'citation("pROC")' for a citation.
## 
## Attachement du package : 'pROC'
## Les objets suivants sont masqués depuis 'package:stats':
## 
##     cov, smooth, var
p1 <- predict(ArbreChoisi, newdata=base_modelisation[-nb_lignes,], type= "prob")[,1]


length(base_modelisation[-nb_lignes,]$category)
## [1] 2000
auc(base_modelisation[-nb_lignes,]$category, p1)
## Setting levels: control = culture, case = economie
## Setting direction: controls > cases
## Area under the curve: 0.8751

Un Auc de 0.88 a été obtenu avec notre jeu test.

#Visualisation de la prédiction

plot(p1 ~ category, data=base_modelisation[-nb_lignes,], xlab="Observe",
       ylab="Predis")

Deuxième modèle : Random Forest

Modélisation : Random Forest, algorithme de bagging

Le principe est de créer n arbres non corrélés entre eux puis faire voter chacun d’entre eux.

Pour faire varier un arbre on sélectionne une partie différente des données à chaque noeud et ne construisant des arbres que sur une partie des individus

Nous commencons avec les paramètres suivants : - mtry : 20 - nbtree: 100

Le paramètre mtry représente le nombre de variables échantillonnées de façon aléatoire comme candidats à chaque fractionnement. et nbtree est le nombre d’arbres générés.

#proximité entre les lignes calculés

 
                          
modele_rf = randomForest(category~. 
                         , data=base_modelisation[nb_lignes,],
                         importance = T,
                         proximity=TRUE,
                         ntree = 100)

plot(modele_rf)

#modele_rf <- randomForest(x=base_modelisation[nb_lignes,-58],
#                          y = base_modelisation[nb_lignes,58],
 #                         ntree=100
                          #,proximity=TRUE

#print(modele_rf)
#modele_rf
#plot(modele_rf)

Prediction

p2 <- predict(modele_rf, newdata=base_modelisation[-nb_lignes,], type= "prob")[,1]

Test Prediction

table(p2, base_modelisation[-nb_lignes,]$category)[1,]
##   culture  economie   planete politique   societe     sport 
##         0         0         0         3         2         0

Fréquence conditionel

table(predict(modele_rf), base_modelisation[nb_lignes,]$category)
##            
##             culture economie planete politique societe sport
##   culture      1547      156     144        93     325   157
##   economie       71      867     124       109     194    19
##   planete         1        0      50         2       2     2
##   politique      32       79      40       642     144    11
##   societe       141      264     220       257    1611    82
##   sport          20       15       4         5      14   556
#plot(margin(modele_rf, base_modelisation[-nb_lignes,]$category))

AUC

length(base_modelisation[-nb_lignes,]$category)
## [1] 2000
auc(base_modelisation[-nb_lignes,]$category, p2)
## Warning in roc.default(response, predictor, auc = TRUE, ...): 'response'
## has more than two levels. Consider setting 'levels' explicitly or using
## 'multiclass.roc' instead
## Setting levels: control = culture, case = economie
## Setting direction: controls > cases
## Area under the curve: 0.9295

Un AUC de 0.92 a été obtenu avec notre jeu de test.

#Visualisation de la prédiction

plot(p2 ~ category, data=base_modelisation[-nb_lignes,], xlab="Observe",
       ylab="Predis")

Troisième modèle : Validation croisée

Ce modèle nécessite de réaliser plusieurs modèles d’un des modèles précédent, nous choisissons le modèle de random Forest, observons combien faut-il de temps pour calculer 100 arbres à mon ordinateur.

debut <- Sys.time()

cent = randomForest(category~. 
                         , data=base_modelisation[nb_lignes,],
                         importance = T,
                         ntree = 100)
TempsCent <- Sys.time() - debut
print(paste("Pour cent arbres, il faut : ", TempsCent))
## [1] "Pour cent arbres, il faut :  2.34681801398595"

1 minute et 45 secondes !

En 3h, il y a 180 minutes, je peux donc générer 10 000 arbres en 3h. et en 20 minutes je peux en calculer 1000. Commençons par l’option à 1000 arbres.

Créons plusieurs modèle avec des mtry allant de 1 variables à toutes. En tout 50 configurations seront testés.

mtry_expand = expand.grid( .mtry = seq(from = 1, to = (ncol(base_modelisation[nb_lignes,])-1), length.out = 50))
#length.out : premier multiplieur

On créé un grand nombre d’arbres par random forest, avec des configurations différentes du mtry, et grace à la librairie doSNOW on execute 4 fois le code afin d’obtenir une validation croisée.

require(caret)
require(doSNOW)
## Le chargement a nécessité le package : doSNOW
## Le chargement a nécessité le package : foreach
## Le chargement a nécessité le package : iterators
## Le chargement a nécessité le package : snow
#parametre du cv
cv.cntrl <- trainControl(method = "cv", 
                           number = 4, 
                           search = "grid")
  


 #on cree des instances , càd le nbre de fois que l'on execute le programme,
#mon processeur a 4 coeurs, je mets donc 4,
# il s'agira donc d'une validation croisée de degré 4.
# il s'agit de notre deuxième multiplieur
  cl <- makeCluster(4, 
                    type = "SOCK") 
  registerDoSNOW(cl)
  
  
  set.seed(1234)
 
  #méthode CART
 #   modele3 <- train(x = base_modelisation[nb_lignes,][,names(base_modelisation[nb_lignes,]) != 'category'],
  #                    y = base_modelisation[nb_lignes,]$category,
   #                   method = 'rpart', trControl = cv.cntrl, 
    #                  tuneGrid = mtry_expand, metric = "Accuracy")
  
  
  #méthode random forest 
    modele3 <- train(x = base_modelisation[nb_lignes,][,names(base_modelisation[nb_lignes,]) != 'category'],
                      y = base_modelisation[nb_lignes,]$category, 
                      method = 'rf', trControl = cv.cntrl, 
                      tuneGrid = mtry_expand, metric = "Accuracy",
                      ntree = 5)
    #ntree est notre dernier multiplieur.
  
 
  stopCluster(cl)
#On calcule ainsi length.out x nbre de clust x ntree = nbre d'arbres de notre modèle
  #                 50       x     4         x 5     = 1000

Quel est le meilleur paramètre pour mtry

modele3_mtry <- modele3$bestTune$mtry
#modele3_best <- modele3$results %>% filter(mtry==modele3_mtry)

#le meilleur mtry est de :
modele3_mtry
## [1] 376.4286

On affiche le modèle obtenu

plot(modele3)

plot(modele3$finalModel$predicted)

Prédiction

library(ROCR)
library(pROC)

p3 <- predict(modele3, newdata=base_modelisation[-nb_lignes,], type= "prob")
p3 <- p3[,1]

Matrice de confusion

MatriceConfu3 <- confusionMatrix(data = modele3$finalModel$predicted,
                                reference = base_modelisation[nb_lignes,]$category)


#labels <- c("Precision", "Recall", "F1", "Accuracy", "Kappa")
#confu3 <- MatriceConfu3$byClass[labels[1:3]]
#confu3 <- c(confu3, MatriceConfu3$overall[labels[4:5]])

MatriceConfu3
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  culture economie planete politique societe sport
##   culture      1207      178     148       127     402   161
##   economie      121      578     105       123     285    57
##   planete        46       95      92        47     120    19
##   politique      58      129      45       458     251    28
##   societe       165      233     103       230     968    58
##   sport          35       28      20        17      53   418
## 
## Overall Statistics
##                                           
##                Accuracy : 0.5162          
##                  95% CI : (0.5046, 0.5278)
##     No Information Rate : 0.2884          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.3942          
##                                           
##  Mcnemar's Test P-Value : < 2.2e-16       
## 
## Statistics by Class:
## 
##                      Class: culture Class: economie Class: planete
## Sensitivity                  0.7396         0.46575        0.17934
## Specificity                  0.8178         0.88420        0.95116
## Pos Pred Value               0.5430         0.45548        0.21957
## Neg Pred Value               0.9147         0.88837        0.93799
## Prevalence                   0.2264         0.17217        0.07117
## Detection Rate               0.1675         0.08019        0.01276
## Detection Prevalence         0.3084         0.17605        0.05813
## Balanced Accuracy            0.7787         0.67497        0.56525
##                      Class: politique Class: societe Class: sport
## Sensitivity                   0.45709         0.4656      0.56410
## Specificity                   0.91766         0.8462      0.97634
## Pos Pred Value                0.47265         0.5509      0.73205
## Neg Pred Value                0.91281         0.7962      0.95133
## Prevalence                    0.13901         0.2884      0.10280
## Detection Rate                0.06354         0.1343      0.05799
## Detection Prevalence          0.13443         0.2438      0.07922
## Balanced Accuracy             0.68737         0.6559      0.77022

AUC

length(base_modelisation[-nb_lignes,]$category)
## [1] 2000
auc(base_modelisation[-nb_lignes,]$category, p3)
## Warning in roc.default(response, predictor, auc = TRUE, ...): 'response'
## has more than two levels. Consider setting 'levels' explicitly or using
## 'multiclass.roc' instead
## Setting levels: control = culture, case = economie
## Setting direction: controls > cases
## Area under the curve: 0.8696

Un Auc de 0.89 a été trouver avec notre jeu de test, c’est moins bien que notre deuxième modèle. Il doit exister un moyen d’optimiser cela.

#Visualisation de la prédiction

plot(p3 ~ category, data=base_modelisation[-nb_lignes,], xlab="Observe",
       ylab="Predis")

Comparaison modèle

A REDIGER

Mise en œuvre d’un modèle supervisé avec maximum 25 variables

On sélectionne les 25 variables les plus importantes parmis le 2ème modèle (random Forest) Ainsi qu’une visualisation graphique de leur importance.

#class (modele_rf$importance[order(modele_rf$importance[,1], decreasing = TRUE)[1:25], ])
#"matrix" "array" 

modele_rf$importance[order(modele_rf$importance[,1], decreasing = TRUE)[1:25], ]
##              culture      economie       planete     politique       societe
## film      0.03779862  0.0101769627  1.970205e-03  6.917695e-03  0.0052767906
## selon     0.03682235 -0.0033766209  5.969326e-04  6.722209e-04 -0.0040805528
## loi       0.03510184  0.0018723104  8.776673e-04  1.176114e-02 -0.0020204313
## president 0.03254492  0.0025733510 -6.818821e-04  4.018785e-03  0.0001095394
## entrepris 0.03059897  0.0108422843  6.059467e-05  4.510012e-03  0.0028626525
## euros     0.02911506  0.0073409536  2.131236e-04  4.220319e-03  0.0018500476
## scen      0.02606563  0.0095775789  3.206675e-03  2.670347e-03  0.0052180034
## ete       0.02551715  0.0024753406 -5.947861e-03  1.764606e-03 -0.0018503198
## ministr   0.02441432  0.0080520537  1.842779e-03  6.285404e-03 -0.0019171661
## festival  0.02195785  0.0044679688  1.761555e-03  4.984302e-03  0.0062098429
## contr     0.01873302  0.0034327927  1.341459e-03  4.206246e-04 -0.0015698615
## art       0.01850906  0.0044264773  2.212927e-04  2.814519e-03  0.0050032630
## plus      0.01792733 -0.0011945951 -1.299601e-03 -1.770111e-04 -0.0030120290
## franc     0.01753846  0.0002589011 -2.659020e-03  8.116099e-04 -0.0009798408
## person    0.01704772 -0.0003942590  1.412098e-03  1.604334e-03  0.0001234612
## econom    0.01665677 -0.0005294370  1.538815e-03  5.030611e-03  0.0025616114
## equip     0.01569333  0.0008414676  5.739601e-04  3.058271e-03  0.0028193848
## droit     0.01553114  0.0013362256  9.065263e-04  6.537585e-03 -0.0026760642
## match     0.01473064  0.0054755511  3.172074e-03  2.928522e-03  0.0057535165
## gouvern   0.01432893  0.0030974688  4.399077e-04  8.084119e-03 -0.0006023529
## mard      0.01386552  0.0008791362 -1.404762e-03  1.869725e-05 -0.0045630821
## general   0.01277326 -0.0020200683  1.264251e-04  3.992878e-04 -0.0001867260
## etat      0.01189911 -0.0012837380  4.058272e-04  3.909779e-04 -0.0003448217
## final     0.01152647  0.0030227986  1.083431e-03  8.666551e-04  0.0014849358
## salar     0.01098601  0.0011872891  3.687099e-05  2.829448e-03  0.0005789450
##                   sport MeanDecreaseAccuracy MeanDecreaseGini
## film       9.323085e-03          0.013904239        121.73817
## selon      6.583245e-03          0.007406092         33.41306
## loi        1.554878e-02          0.011009433         54.11748
## president  3.594765e-03          0.008718757         37.66749
## entrepris  1.444357e-02          0.011737164         66.43167
## euros      5.516489e-03          0.009565224         48.31213
## scen       3.160252e-03          0.009967536         96.50719
## ete        3.289099e-04          0.005526061         37.21730
## ministr    9.819286e-03          0.008306189         45.13795
## festival   4.807438e-03          0.008828925         87.73417
## contr      1.305475e-03          0.004689947         29.85610
## art        5.491040e-03          0.007360796         66.01131
## plus      -1.546640e-03          0.002761880         31.31160
## franc     -3.438295e-03          0.003292100         30.83563
## person     2.789247e-03          0.004411981         26.40602
## econom     6.465474e-03          0.005890631         34.49277
## equip      2.736063e-02          0.007805329         52.61598
## droit      9.508637e-05          0.003970888         25.68902
## match      7.857125e-02          0.014721566        111.23249
## gouvern    7.610276e-03          0.005542942         33.19277
## mard      -3.666819e-04          0.001842740         14.97844
## general    1.635510e-03          0.002719767         18.64439
## etat       2.716745e-03          0.002734150         21.34128
## final      1.743296e-02          0.005540303         46.79316
## salar      2.770249e-03          0.003551707         21.84534
varImpPlot(modele_rf)

 #copie des termes dans l'attente de trouver une méthode pour récup les variables d'une matrice.                         
modele_25 = randomForest(category~ selon + film + loi + entrepris + president + ete + ministr + festival + scen + gouvern + contr + franc + match + person + art + general + social + equip + droit + national + etat + final + econom + plac
                         , data=base_modelisation[nb_lignes,],
                         importance = T,
                         proximity=TRUE,
                         ntree = 100)

plot(modele_25)

Prediction

p4 <- predict(modele_25, newdata=base_modelisation[-nb_lignes,], type= "prob")[,1]

Test Prediction

table(p4, base_modelisation[-nb_lignes,]$category)[1,]
##   culture  economie   planete politique   societe     sport 
##         2        24         7        23        21        29

Fréquence conditionel

table(predict(modele_25), base_modelisation[nb_lignes,]$category)
##            
##             culture economie planete politique societe sport
##   culture      1487      256     171       122     429   186
##   economie      100      644      94       113     273    38
##   planete         3        2       5         7      11     2
##   politique      32      116      55       476     234    16
##   societe       165      324     250       379    1311    76
##   sport          25       39       7        11      32   509

AUC

length(base_modelisation[-nb_lignes,]$category)
## [1] 2000
auc(base_modelisation[-nb_lignes,]$category, p4)
## Warning in roc.default(response, predictor, auc = TRUE, ...): 'response'
## has more than two levels. Consider setting 'levels' explicitly or using
## 'multiclass.roc' instead
## Setting levels: control = culture, case = economie
## Setting direction: controls > cases
## Area under the curve: 0.8803

Une perte d’environ 0.006 d’AUC pour un passage de 440 variables à 25. Cette perte est négligable

#Visualisation de la prédiction

plot(p2 ~ category, data=base_modelisation[-nb_lignes,], xlab="Observe",
       ylab="Predis")

Conclusion

Nos 3 modèles sont utilisables. La forte réduction du nombre de variable sur le modèle randomForest a eu un impact mineur sur l’AUC.